package EvaTCPL;
import RKUjava.util.*;

public class EvaTCPLVariable
{
  /** The identifier for this variable */
  String identifier;

  /** The problem this variable is a part of. */
  EvaTCPLProblem theproblem;

  /** Type of variable:
      0 = evar
      1 = svar
      2 = cvar
      3 = pvar
  */
  int type;

  /** Domain type
      -1 = not initialized
      0  = real
      1  = int
      2  = class
  */
  public int domaintype = -1;

  /** Current value (if domain=real) */
  public double real_val;
  public double real_min, real_max;

  /** Current value (if domain=int) */
  public int int_val;
  public int int_min, int_max;

  // Stochasticity
  public EvaTCPLaexpression stoch_function;
  public int stoch_init = -1; //-1= not initialized, 0 = calculate, 1 = value
  public double stoch_initrealval = 0;
  public int stoch_initintval = 0;
  public double stoch_realval = 0;
  public int stoch_intval = 0;

  // Periodicity
  public int period_defined = 0; // Are we using periodicity? 0= no, 1=yes
  public double[] period_sinamp, period_sinper, period_cosamp, period_cosper;
  public double period_timezero;
  public double period_time;
  public boolean period_constantadvtime = false; // true if the timeadvancement function is a constant function 
  public EvaTCPLaexpression period_timeadvance = new EvaTCPLintvalue(1);
  public double period_initrealval;
  public int period_initintval;
  public double period_realval;
  public int period_intval;

  // Drift
  public EvaTCPLaexpression drift_function;
  public int drift_init = -1; //-1= not initialized, 0 = calculate, 1 = value
  public EvaTCPLaexpression drift_lengthfunction;
  public int drift_time;
  public int drift_length;
  public double drift_initrealval = 0;
  public int drift_initintval = 0;
  public double drift_realval;
  public int drift_intval;

  // Dependency
  public EvaTCPLaexpression depend_function;
  private double new_realval;
  private int new_intval;

  // Delay
  public int delay_length = 0;
  public EvaTCPLCircDoubleList delay_realvals;
  public Double[] delay_initvals;

  // External Store
  public int estore_length = 0;
  public EvaTCPLCircDoubleList estore_realvals;
  public Double[] estore_initvals;

  // Internal Store
  public int istore_length = 0;
  public EvaTCPLCircDoubleList istore_realvals;
  public Double[] istore_initvals;

  // Init
  public int init_init = -1; //-1= not initialized, 0 = calculate, 1 = value
  public int init_length = 0;
  public EvaTCPLaexpression init_function;

  // Measure noise
  public EvaTCPLaexpression mnoise_function;

  // Some variables to help debugging
  public int lineno_vardef;
  public int lineno_domaindef;
  public int lineno_stochdef;
  public int lineno_dependdef;
  public int lineno_perioddef;
  public int lineno_driftdef;
  public int lineno_delaydef;
  public int lineno_estoredef;
  public int lineno_istoredef;
  public int lineno_initdef;
  public int lineno_mnoisedef;

  public EvaTCPLVariable(String theidentifier, int thetype)
    {
      identifier = theidentifier;
      type = thetype;
      period_sinamp = new double[0];
      period_sinper = new double[0];
      period_cosamp = new double[0];
      period_cosper = new double[0];
    }

  /** Get an externally stored value. (used by varname[-x])
      @param index The index of the stored value. -1 is the most recent
      value, i.e., the value of the variable in the previous timestep.
  */
  public double getEStoreRealValue(int index)
    {
      return estore_realvals.getValue(index+1);
    }

  public int getEStoreIntValue(int index)
    {
      return 0;
    }

  public int getEStoreClassValue(int index)
    {
      return 0;
    }

  /** Get an internally stored value. (used by this[-x])
      @param index The index of the stored value. -1 is the most recent
      value, i.e., the value of the variable in the previous timestep.
  */
  public double getIStoreRealValue(int index)
    {
      return istore_realvals.getValue(index);
    }

  public int getIStoreIntValue(int index)
    {
      return 0;
    }

  public int getIStoreClassValue(int index)
    {
      return 0;
    }


  public double getCurrentRealValue()
    {
      return estore_realvals.getValue(-1);
    }

  public int getCurrentIntValue()
    {
      return 0;
    }

  public int getCurrentClassValue()
    {
      return 0;
    }

  /** Calculate value for the variable. */
  public void prepareUpdate()
    {
      switch (domaintype) {
      case 0: // real
	new_realval = calcRealValue();
	break;
      case 1: // int
	break;
      case 2: // class
	break;
      }
    }

  /** Update Variable with calculated value. */
  public void finalizeUpdate()
    {
      // Update stored values
      switch (domaintype) {
      case 0: // Real
	if (delay_length==0) {
	  real_val = new_realval;
	}
	else {
	  real_val = delay_realvals.getLast();
	  delay_realvals.prepend(new_realval);
	}
	estore_realvals.append(real_val);
	istore_realvals.append(new_realval);
        break;
      case 1: // Int
        break;
      case 2: // Class
	break;
      }
    }
      
  
  private double calcRealValue()
    {
      double res;
      int i; 

      // Stochasticity calculation
      if (stoch_function!=null) {
	stoch_realval = stoch_function.evaluate();
      }

      // Periodicity calculation
      if (period_defined>0) {
	period_realval = 0;

	for (i=0;i<period_cosamp.length;i++) {
	  period_realval += 
	    period_cosamp[i]*Math.cos((Math.PI*2*period_cosper[i])*period_time);
	}

	for (i=0;i<period_sinamp.length;i++) {
	  period_realval += 
	    period_sinamp[i]*Math.sin((Math.PI*2*period_sinper[i])*period_time);
	}
	if (period_constantadvtime) {
	  period_time = period_timezero + (1+theproblem.currenttime)*period_timeadvance.evaluate();
	}
	else {
	  period_time += period_timeadvance.evaluate();
	}

	System.out.println("period time:"+period_time);
      }

	
      if (drift_function!=null) {
	drift_realval = drift_function.evaluate();
	drift_time += 1;
	if (drift_lengthfunction!=null && drift_time>drift_length) {
	  drift_length = (int)Math.rint(drift_lengthfunction.evaluate());
	  drift_time = 0;
	}
      }
      res = depend_function.evaluate();

      return res;
    }


  public void initialize()
    {
      int i;

      if (estore_length==0)
	estore_length = 1;
      if (istore_length==0)
	istore_length = 1;
      if (init_length==0)
	init_length =	1;

      switch (domaintype) {
      case 0: // Real
	estore_realvals = new EvaTCPLCircDoubleList(estore_length);
	istore_realvals = new EvaTCPLCircDoubleList(istore_length);
	delay_realvals = new EvaTCPLCircDoubleList(delay_length);
	break;
      case 1: // Int
	break;
      case 2: // Class
	break;
      }

      // Function initialization
      if (stoch_function!=null) {
	stoch_function.initialize(theproblem);
      }

      if (drift_function!=null) {
	drift_function.initialize(theproblem);
      }

      if (drift_lengthfunction!=null) {
	drift_lengthfunction.initialize(theproblem);
      }

      period_timeadvance.initialize(theproblem);

      // Check if the timeadvancement function is constant.
      // Then we can avoid rounding errors.
      if (period_timeadvance instanceof EvaTCPLintvalue ||
	  period_timeadvance instanceof EvaTCPLdoublevalue) {
	period_constantadvtime = true;
      }
      else {
	period_constantadvtime = false;
      }	

      depend_function.initialize(theproblem);

      if (mnoise_function!=null) {
	mnoise_function.initialize(theproblem);
      }

      // Initialization of variables
      switch (domaintype) {
      case 0: // Real

	// ESTOREDEF: Init of estore array
	if (estore_initvals!=null) {
	  for (i=0;i<estore_initvals.length;i++) {
	    estore_realvals.append(estore_initvals[i].doubleValue());
	  }
	}

	// ISTOREDEF: Init of istore array
	if (istore_initvals!=null) {
	  for (i=0;i<istore_initvals.length;i++) {
	    istore_realvals.append(istore_initvals[i].doubleValue());
	  }
	}

	// DELAYDEF: Init of delay array
	if (delay_initvals!=null) {
	  for (i=0;i<delay_initvals.length;i++) {
	    delay_realvals.append(delay_initvals[i].doubleValue());
	  }
	}

	// Specific periodicity variables
	period_time = period_timezero;

	// Specific drift variables
	if (drift_lengthfunction!=null) {
	  drift_length = (int)Math.rint(drift_lengthfunction.evaluate());
	}
	drift_time = 0;

	// INITDEF: Init of vars value
	switch (init_init) {
	case -1: // No initialization
	  break;
	case 0: // Calculate
	  for (i=0;i<init_length;i++) {
	    if (delay_length==0) {
	      new_realval = calcRealValue();
	      real_val = new_realval;
	    }
	    else {
	      new_realval = calcRealValue();
	      real_val = delay_realvals.getLast();
	      delay_realvals.prepend(new_realval);
	    }
	    estore_realvals.append(real_val);
	    istore_realvals.append(new_realval);
	  }
	  break;
	case 1: // From value
	  for (i=0;i<init_length;i++) {
	    if (delay_length==0) {
	      new_realval = init_function.evaluate();
	      real_val = new_realval;
	    }
	    else {
	      new_realval = init_function.evaluate();
	      real_val = delay_realvals.getLast();
	      delay_realvals.prepend(new_realval);
	    }
	    estore_realvals.append(real_val);
	    istore_realvals.append(new_realval);
	  }
	  break;
	}

	break;
      case 1: // Int
	break;
      case 2: // Class
	break;
      }
	
    }


  public String toString()
    {
      StringBuffer res = new StringBuffer();

      res.append(identifier);
      res.append(" = ");
      switch (domaintype) {
      case 0: // Real
	res.append(getCurrentRealValue());
        break;
      case 1: // Int
	res.append(getCurrentIntValue());
        break;
      case 2: // Class
	res.append(getCurrentClassValue());
	break;
      }
      res.append("\n");

      if (theproblem.verbose>1) {
	if (estore_length>1) {
	  res.append("  ESTORE:  ");
	  res.append(EvaTCPLutils.arrayToString(estore_realvals.toArray()));
	  res.append("\n");
	}
	
	if (istore_length>1) {
	  res.append("  ISTORE:  ");
	  res.append(EvaTCPLutils.arrayToString(istore_realvals.toArray()));
	  res.append("\n");
	}
	
	if (delay_length>0) {
	  res.append("  DELAY:   ");
	  res.append(EvaTCPLutils.arrayToString(delay_realvals.toArray()));
	  res.append("\n");
	}
      }

      return res.toString();
    }

  /** Check that this variable is legally declared. */
  public void checkVariable(EvaTCPLProblem aproblem)
    {
      StringBuffer errormsg = new StringBuffer();
      String tmpstr;
      int errors = 0;
      int warnings = 0;

      EvaTCPLaexprDepVisitor depvisitor = new EvaTCPLaexprDepVisitor(this);

      theproblem = aproblem;

      // Dependency
      if (depend_function!=null) {
	depend_function.visitorAccept(depvisitor);
	errors += depvisitor.errors;
	warnings += depvisitor.warnings;
	tmpstr = depvisitor.errormsg.toString();
	if (!tmpstr.equals("")) {
	  theproblem.errormsgs.append(tmpstr);
	}
      }
      else {
	theproblem.errormsgs.append(identifier+": Dependency function not defined for this variable. (Near line "+lineno_vardef+").\n");
      }

      //      return errormsg.toString();
    }

  /*  
      switch (domaintype) {
      case 0: // Real
        break;
      case 1: // Int
        break;
      case 2: // Class
	break;
      }
      */
}







